/*
* ECARX Technology Limited is the owner of the copyright and the trade secret of this software.
* Without permission, no one has the right to obtain, disclose or use this software in any way.
*/

package ecarx.xsf.notificationcenter.push;

import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.Log;

import androidx.annotation.IntDef;

import com.google.gson.Gson;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;

import ecarx.eas.notification.ITyrantAction;
import ecarx.eas.notification.StatusBarTyrantNotification;
import ecarx.xsf.notificationcenter.LauncherControllerApp;
import ecarx.xsf.notificationcenter.NotificationCenterData;
import ecarx.xsf.notificationcenter.base.push.model.payload.PushMsgContent;
import ecarx.xsf.notificationcenter.base.push.model.payload.PushMsgEvent;
import ecarx.xsf.notificationcenter.base.push.model.payload.PushMsgModel;
import ecarx.xsf.notificationcenter.base.push.model.payload.PushMsgParams;
import ecarx.xsf.notificationcenter.base.push.model.payload.PushMsgStyle;
import ecarx.xsf.notificationcenter.base.push.model.payload.PushSmartCard;
import ecarx.xsf.notificationcenter.common.Constants;
import ecarx.xsf.notificationcenter.manager.NotificationDispatchManager;
import ecarx.xsf.notificationcenter.scenecard.IPendingIntentAction;
import ecarx.xsf.notificationcenter.scenecard.ISceneCardInfoV2;
import ecarx.xsf.notificationcenter.sdk.IStatusBarAction;
import ecarx.xsf.notificationcenter.sdk.IStatusBarNotification;
import ecarx.xsf.notificationcenter.utils.AppUtils;

/**
 * FileName: PushMsgParser.java <br>
 * Description : 消息推送解析接口<br>
 * Create Time: 2019/12/25 17:17 <br>
 * Update Time: 2021/8/3 14:07 <br>
 *
 * @since 1.0.0
 */
public class PushMsgParser {

    private static final boolean DEBUG = false;
    private static final String TAG = "PushMsgParser";
    private static final String PARAM_TYPE_KEY = "type";
    private static final String PARAM_TYPE_SERVICE = "service";
    private static final String PARAM_TYPE_RECEIVER = "receiver";

    private static final String PARAM_BTN_KEY = "button";
    private static final String ON = "1";

    private static final int TYPE_ACTIVITY = 0;
    private static final int TYPE_SERVICE = 1;
    private static final int TYPE_RECEIVER = 2;

    private static final String RECOMMENDATIONS_KEY = "recommendations_egg_uri_key";


    public static final int STRING_ID_LEN = 5;

    private static final Gson sGson = new Gson();
    private final Context mContext;
    private final String mPushMsg;
    private PushMsgModel mPushMsgModel;

    private String title = "";
    private String text = "";
    private String pkg = "";
    private long timestamp;

    private String mPushMsgId;

    @IntDef({TYPE_ACTIVITY, TYPE_SERVICE, TYPE_RECEIVER})
    @Retention(RetentionPolicy.SOURCE)
    public @interface Type {

    }

    private PushMsgParser(Context context, String pushMsg) {
        mContext = context;
        mPushMsg = pushMsg;
    }

    public static PushMsgParser newInstance(Context context, String response) {
        Log.d(TAG, "push response " + response);
        PushMsgParser parser = new PushMsgParser(context, response);
        return parser;
    }

    private PushMsgModel parse(String json) {
        mPushMsgModel = sGson.fromJson(json, PushMsgModel.class);
        return mPushMsgModel;
    }

    /**
     * 包名首先从 sourcePackage 获取，如果为null，则从event[0] 获取，如果两处都获取不到则该消息无效
     *
     * @return 包名
     */
    private String getSourcePackage() {
        try {
            PushMsgModel model = parse(mPushMsg);
            if (model == null) {
                return null;
            }
            final String sourcePkg = model.getSourcePackage();
            if (TextUtils.isEmpty(sourcePkg)) {
                List<PushMsgEvent> events = model.getEvent();
                if (events == null || events.size() == 0) {
                    return null;
                } else {
                    // 通知条
                    PushMsgEvent notifyEvent = events.get(0);
                    pkg = notifyEvent.getPkg();
                }
            } else {
                pkg = sourcePkg;
            }
            Log.d(TAG, "getSourcePackage: " + pkg);

        } catch (Exception e) {
            e.printStackTrace();
            Log.d(TAG, "getSourcePackage: " + e.toString());
        }
        return pkg;
    }

    private String getPushMsgId() {
        String id;
        if (mPushMsgModel == null) {
            id = constructFakeMsgId();
        } else {
            id = mPushMsgModel.getId();
        }
        if (TextUtils.isEmpty(id)) {
            id = constructFakeMsgId();
        }
        mPushMsgId = id;
        Log.i(TAG, "getPushMsgId: " + mPushMsgId);
        return mPushMsgId;
    }

    private String constructFakeMsgId() {
        return "nc-" + timestamp;
    }

    public void sendNotification() {
        // 使用前初始化一些参数
        final String pkg = getSourcePackage();
        timestamp = System.currentTimeMillis();
        mPushMsgId = getPushMsgId();
        convertNotification(pkg);
    }

    public ISceneCardInfoV2 convertSceneCardInfo(String pkgName, PushMsgModel model) {
        String packageName = pkgName;
        PushMsgModel pushMsgModel = model;
        if (TextUtils.isEmpty(packageName) && pushMsgModel == null) {
            pushMsgModel = parse(mPushMsg);
            packageName = getSourcePackage();
        }
        return getSceneCardInfo(packageName, pushMsgModel);
    }

    private ISceneCardInfoV2 getSceneCardInfo(String pkgName, PushMsgModel model) {
        int ID = 0;
        try {
            if (model.getId() != null && model.getId().length() > STRING_ID_LEN) {
                ID = Integer.parseInt(model.getId().substring(model.getId().length() - STRING_ID_LEN));
            } else {
                ID = Integer.parseInt(model.getId());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        String tts = model.getTts();
        boolean isTtsOn = false;
        try {
            if (!TextUtils.isEmpty(tts)) {
                if (ON.equals(tts)) {
                    isTtsOn = true;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        String beep = model.getBeep();
        boolean isBeep = false;

        try {
            if (!TextUtils.isEmpty(beep)) {
                if (ON.equals(beep)) {
                    isBeep = true;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        String seesay = model.getSeesay();
        boolean seeSay = false;

        try {
            if (!TextUtils.isEmpty(seesay)) {
                if (ON.equals(seesay)) {
                    seeSay = true;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        String tag = model.getTag();
        PushMsgContent contents = model.getContent();
        String title = contents.getTitle();
        String subtitle = contents.getText();
        PushSmartCard smartcard = model.getSmartcard();
        String detailImg = contents.getDetailImg();
        String background = model.getBackground();
        List<PushMsgEvent> event = model.getEvent();
        List<PushMsgEvent> seesayevent = model.getSeesayevent();
        List<PushMsgEvent> cardevent = new ArrayList<>();
        if (smartcard != null) {
            cardevent = smartcard.getCardevent();
        }
        List<IPendingIntentAction> eventsActions = getSceneActions(event, false);
        List<IPendingIntentAction> cardActions = getSceneActions(cardevent, false);
        List<IPendingIntentAction> seeSayActions = getSceneActions(seesayevent, true);
        return new ISceneCardInfoV2(ID, pkgName, isBeep, isTtsOn, seeSay, title, subtitle, background, detailImg, tag, System.currentTimeMillis(), eventsActions, seeSayActions, cardActions);
    }

    public IStatusBarNotification convertTipsInfo(String pkgName, PushMsgModel model) {
        int ID = 0;
        try {
            if (model.getId() != null && model.getId().length() > STRING_ID_LEN) {
                ID = Integer.parseInt(model.getId().substring(model.getId().length() - STRING_ID_LEN));
            } else {
                ID = Integer.parseInt(model.getId());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        String tts = model.getTts();
        boolean isTtsOn = false;
        try {
            if (!TextUtils.isEmpty(tts)) {
                if (ON.equals(tts)) {
                    isTtsOn = true;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        String beep = model.getBeep();
        boolean isBeep = false;

        try {
            if (!TextUtils.isEmpty(beep)) {
                if (ON.equals(beep)) {
                    isBeep = true;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        String seesay = model.getSeesay();
        boolean seeSay = false;

        try {
            if (!TextUtils.isEmpty(seesay)) {
                if (ON.equals(seesay)) {
                    seeSay = true;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        String tag = model.getTag();
        PushMsgContent msgContent = model.getContent();
        String title = msgContent.getTitle();
        String content = msgContent.getText();
        String landingPageUrl = msgContent.getDetailUrl();
        List<PushMsgEvent> event = model.getEvent();
        List<PushMsgEvent> seesayevent = model.getSeesayevent();
        List<IStatusBarAction> actions = getStatusActions(event, false);
        List<IStatusBarAction> seeSayActions = getStatusActions(seesayevent, true);

        //如果 event 只有一个，且 action 为空则认为是轻气泡提醒
        String subStyle = Constants.ICardStyle.STYLE_HEAVY;
        if (event != null && event.size() == 1) {
            PushMsgEvent pushMsgEvent = event.get(0);
            if (!TextUtils.isEmpty(pushMsgEvent.getAction())) {
                subStyle = Constants.ICardStyle.STYLE_LIGHT;
            }
        }
        return new IStatusBarNotification(ID, pkgName, Constants.STYLE_SMART_TIP, title, content, null, null, null, false, isTtsOn,
                seeSay, isBeep, landingPageUrl, actions, seeSayActions, null, subStyle, tag, System.currentTimeMillis());
    }

    private List<IPendingIntentAction> getSceneActions(List<PushMsgEvent> event, boolean seesay) {
        List<IPendingIntentAction> actions = new ArrayList<>();

        if (event != null) {
            for (int i = 0; i < event.size(); i++) {
                String text = "";
                PushMsgEvent pushMsgEvent = event.get(i);
                if (!seesay) {
                    List<PushMsgParams> params = pushMsgEvent.getParams();
                    if (params != null) {
                        for (PushMsgParams p : params) {
                            if (PARAM_BTN_KEY.equals(p.getKey())) {
                                text = p.getValue();
                            }
                        }
                    }
                } else {
                    text = pushMsgEvent.getText();
                }
                PendingIntent pendingIntent = buildPendingIntent(pushMsgEvent);
                String bgUrl = pushMsgEvent.getBgUrl();
                String functionId = pushMsgEvent.getFunctionId();
                IPendingIntentAction action = new IPendingIntentAction();
                action.setId(i);
                action.setBgUrl(bgUrl);
                action.setFunctionId(functionId);
                action.setPendingIntent(pendingIntent);
                action.setText(text);
                actions.add(action);
            }
        }
        return actions;
    }

    private List<IStatusBarAction> getStatusActions(List<PushMsgEvent> event, boolean seesay) {

        List<IStatusBarAction> actions = new ArrayList<>();
        if (event != null) {
            for (int i = 0; i < event.size(); i++) {
                String text = "";
                PushMsgEvent pushMsgEvent = event.get(i);
                if (!seesay) {
                    List<PushMsgParams> params = pushMsgEvent.getParams();
                    if (params != null) {
                        for (PushMsgParams p : params) {
                            if (PARAM_BTN_KEY.equals(p.getKey())) {
                                text = p.getValue();
                            }
                        }
                    }
                } else {
                    text = pushMsgEvent.getText();
                }
                PendingIntent pendingIntent = buildPendingIntent(pushMsgEvent);
                String bgUrl = pushMsgEvent.getBgUrl();
                String functionId = pushMsgEvent.getFunctionId();
                actions.add(new IStatusBarAction(i, text, bgUrl, functionId, pendingIntent , pushMsgEvent.getClassName()));
            }
        }
        return actions;
    }

    /**
     * * 数据解析.
     * <p> 根据 style 确定消息类型 {@link Constants} <br>
     *
     * @param pkgName 包名，通知消息包不能为空
     * @since 1.0.0
     */
    private void convertNotification(String pkgName) {
        PushMsgModel model = parse(mPushMsg);

        if (model == null) {
            return;
        }
        PushMsgStyle style = model.getStyle();

        int type = Constants.STYLE_NOTIFICATION;
        try {
            Log.d(TAG, "pns push style " + type);
            type = Integer.parseInt(style.getType());
        } catch (Exception e) {
            e.printStackTrace();
        }

        if (type == Constants.STYLE_NOTIFICATION) {
            // 普通通知
            if (!AppUtils.isAppPkgExist(mContext, pkgName)) {
                Log.e(TAG, "package is null, invalid notification");
                pkgName = mContext.getPackageName();
//                return;
            }
            buildNotification(pkgName, false, model);
        } else if (type == Constants.STYLE_PNS_TYRANT) {
            // 霸屏通知
            if (!AppUtils.isAppPkgExist(mContext, pkgName)) {
                Log.e(TAG, "package is null, invalid notification");
                pkgName = mContext.getPackageName();
//                return;
            }
            buildNotification(pkgName, true, model);
        } else if (type == Constants.STYLE_SMART_TIP) {
            // 智能提示
            NotificationDispatchManager.get().dispatch(convertTipsInfo(pkgName, model));
        } else if (type == Constants.STYLE_SMART_CARD) {
            //  智能卡片
            NotificationDispatchManager.get().dispatch(convertSceneCardInfo(pkgName, model));
        }
    }

    /**
     * * 根据 GSON 解析的数据构建通知.
     *
     * @param pkgName  包名
     * @param isTyrant 是否霸屏
     * @param model    数据类
     * @since 1.0.0
     */
    private void buildNotification(String pkgName, boolean isTyrant, PushMsgModel model) {
        PushMsgContent content = model.getContent();
        List<PushMsgEvent> event = model.getEvent();
        PendingIntent notifyPending = null;
        String className = "";
        List<ITyrantAction> actions = new ArrayList<>();

        if (event != null && event.size() > 0) {
            Log.d(TAG, "event build " + event.size());
            className = event.get(0).getClassName();
            notifyPending = buildPendingIntent(event.get(0), content.getTitle());
            if (event.size() > 1) {
                PushMsgEvent event1 = event.get(1);
                PendingIntent event1Pending = buildPendingIntent(event1);
                Map<String, String> map = convertParamsToMap(event1);
                String btnName = getBtnName(map, "按钮1");
                actions.add(new ITyrantAction(1, btnName, event1Pending ,event.get(1).getClassName()));
            }

            if (event.size() > 2) {
                PushMsgEvent event2 = event.get(2);
                PendingIntent event2Pending = buildPendingIntent(event2);
                Map<String, String> map = convertParamsToMap(event2);
                String btnName = getBtnName(map, "按钮1");
                actions.add(new ITyrantAction(2, btnName, event2Pending,event.get(2).getClassName()));
            }
        }
        int ID = 0;
        try {
            if (model.getId() != null && model.getId().length() > STRING_ID_LEN) {
                ID = Integer.parseInt(model.getId().substring(model.getId().length() - STRING_ID_LEN));
            } else {
                ID = Integer.parseInt(model.getId());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        if (DEBUG) {
            Log.d(TAG, "msg key ID = " + ID);
        }
        StatusBarTyrantNotification tyrantNotification = new StatusBarTyrantNotification(ID,
                pkgName,
                content.getTitle(), content.getText(), notifyPending,
                null, System.currentTimeMillis(), isTyrant ,actions , className);
        NotificationCenterData.getInstance().enqueueTyrantNotification(tyrantNotification);
    }

    /**
     * * 根据 MSG Event 构建 Pending Intent.
     * <p> 构建 PendingIntent 时包名不能为空 <br>
     *
     * @param event {@link PushMsgModel}
     * @return PendingIntent
     * @since 1.0.0
     */
    private PendingIntent buildPendingIntent(PushMsgEvent event) {
        return buildPendingIntent(event, "");
    }

    private PendingIntent buildPendingIntent(PushMsgEvent event, String title) {
        if (event == null) {
            Log.e(TAG, "event is null");
            return null;
        }
        String pkg = event.getPkg();
        if (TextUtils.isEmpty(pkg)) {
            Log.e(TAG, "event package is null");
            return null;
        }
        String action = event.getAction();
        String className = event.getClassName();
        String uri = event.getUri();
        List<PushMsgParams> params = event.getParams();

        Intent intent = new Intent();
        intent.putExtra("title" , title);
        if (params != null && params.size() > 0) {
            for (PushMsgParams p : params) {
                if (p != null &&
                        !TextUtils.isEmpty(p.getKey()) &&
                        !PARAM_TYPE_KEY.equals(p.getKey()) && !PARAM_BTN_KEY.equals(p.getKey())) {
                    intent.putExtra(p.getKey(), p.getValue());
                }
            }
        }
        // 包名填错的情况下，setPackage影响intent查找
        if (!TextUtils.isEmpty(pkg)) {
            intent.setPackage(pkg);
        }

        Bundle options = null;
        if (!TextUtils.isEmpty(action)) {
            intent.setAction(action);
            if ("com.ecarx.compositorui".equals(pkg) && "ecarx.intent.action.RECOMMENDATION_PSD_WEBVIEW".equals(action) && LauncherControllerApp.getPsdDisplay() != null) {
                options = new Bundle();
                options.putInt("android.activity.launchDisplayId", LauncherControllerApp.getPsdDisplay().getDisplayId());
            }
        }
        if (!TextUtils.isEmpty(pkg) && !TextUtils.isEmpty(className)) {
            ComponentName componentName;
            if (className.contains("ecarx.notificationcenterui.H5Activity")) {
                componentName = new ComponentName("ecarx.notificationcenterui", className);
            }else{
                componentName = new ComponentName(pkg, className);
            }
            intent.setComponent(componentName);
        }
        if (!TextUtils.isEmpty(uri)) {
            //内容服务彩蛋
            if ("com.ecarx.compositorui".equals(pkg)) {
                intent.putExtra(RECOMMENDATIONS_KEY, uri);
            } else {
                intent.setData(Uri.parse(uri));
            }
        }
        // 解析每一对params的key：value
        Map<String, String> map = convertParamsToMap(event);
        int type = getType(map);
        return getPendingIntentByType(type, intent, options);
    }

    private PendingIntent getPendingIntentByType(@Type int type, Intent intent, Bundle options) {
        PendingIntent pendingIntent = null;
        switch (type) {
            case TYPE_ACTIVITY:
                pendingIntent = PendingIntent.getActivity(mContext, UUID.randomUUID().hashCode(), intent,
                        PendingIntent.FLAG_UPDATE_CURRENT, options);
                break;

            case TYPE_SERVICE:
                pendingIntent = PendingIntent.getService(mContext, UUID.randomUUID().hashCode(), intent,
                        PendingIntent.FLAG_UPDATE_CURRENT);
                break;

            case TYPE_RECEIVER:
                pendingIntent = PendingIntent.getBroadcast(mContext, UUID.randomUUID().hashCode(), intent,
                        PendingIntent.FLAG_UPDATE_CURRENT);
                break;
            default:
                break;
        }
        if (pendingIntent == null) {
            pendingIntent = PendingIntent.getActivity(mContext, UUID.randomUUID().hashCode(), intent,
                    PendingIntent.FLAG_UPDATE_CURRENT);
        }
        return pendingIntent;
    }

    private Map<String, String> convertParamsToMap(PushMsgEvent event) {
        List<PushMsgParams> action1Params = event.getParams();
        Map<String, String> paramMap = new HashMap<>();
        if (action1Params != null && action1Params.size() > 0) {
            for (PushMsgParams param : action1Params) {
                paramMap.put(param.getKey(), param.getValue());
            }
        }
        return paramMap;
    }

    /**
     * 解析params获取跳转类型Activity，Service，Receiver
     */
    private @Type
    int getType(Map<String, String> map) {
        // 解析每一对params的key：value
        int type = TYPE_ACTIVITY;
        if (map.containsKey(PARAM_TYPE_KEY)) {
            String value = map.get(PARAM_TYPE_KEY);
            if (PARAM_TYPE_SERVICE.equals(value)) {
                type = TYPE_SERVICE;
            } else if (PARAM_TYPE_RECEIVER.equals(value)) {
                type = TYPE_RECEIVER;
            }
        }
        return type;
    }

    /**
     * 解析params获取按钮名称
     */
    private String getBtnName(Map<String, String> map, String defaultName) {
        String name = defaultName;
        if (map.containsKey(PARAM_BTN_KEY)) {
            name = map.get(PARAM_BTN_KEY);
        }
        return name;
    }

}
